package com.nidu.demo.oauth2.gatewayimpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.PageQuery;
import com.alibaba.cola.dto.PageResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nidu.demo.oauth2.convertor.OAuth2AccessTokenConvertor;
import com.nidu.demo.oauth2.gateway.OAuth2AccessTokenGateway;
import com.nidu.demo.oauth2.mapper.OAuth2AccessTokenMapper;
import com.nidu.demo.oauth2.mapper.dataobject.OAuth2AccessTokenDO;
import com.nidu.demo.oauth2.model.OAuth2AccessToken;
import com.nidu.demo.oauth2.model.OAuth2AccessTokenCriteria;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class OAuth2AccessTokenGatewayImpl implements OAuth2AccessTokenGateway {

    private final OAuth2AccessTokenMapper mapper;
    private final OAuth2AccessTokenConvertor convertor;

    @Override
    public OAuth2AccessToken create(OAuth2AccessToken accessToken) {
        OAuth2AccessTokenDO accessTokenDO = convertor.toDataObject(accessToken);
        mapper.insert(accessTokenDO);
        return convertor.toDomainObject(accessTokenDO);
    }

    @Override
    public OAuth2AccessToken update(OAuth2AccessToken accessToken) {
        OAuth2AccessTokenDO accessTokenDO = convertor.toDataObject(accessToken);
        mapper.updateById(accessTokenDO);
        return convertor.toDomainObject(accessTokenDO);
    }

    @Override
    public void deleteById(Long id) {
        mapper.deleteById(id);
    }

    @Override
    public void deleteByIds(Set<Long> ids) {
        mapper.deleteByIds(ids);
    }

    @Override
    public OAuth2AccessToken getById(Long id) {
        OAuth2AccessTokenDO oAuth2AccessTokenDO = mapper.selectById(id);
        if (oAuth2AccessTokenDO == null) {
            return null;
        }
        return convertor.toDomainObject(oAuth2AccessTokenDO);
    }

    @Override
    public OAuth2AccessToken getByAccessToken(String accessToken) {
        // 查询token是否存在
        OAuth2AccessTokenDO accessTokenDO = mapper.selectByAccessToken(accessToken);
        if (accessTokenDO == null) {
            return null;
        }
        return convertor.toDomainObject(accessTokenDO);
    }

    @Override
    public List<OAuth2AccessToken> getByRefreshToken(String refreshToken) {
        LambdaQueryWrapper<OAuth2AccessTokenDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
        List<OAuth2AccessTokenDO> accessTokenDOS = mapper.selectList(wrapper);
        if (CollUtil.isEmpty(accessTokenDOS)) {
            return null;
        }
        return convertor.toDomainObjects(accessTokenDOS);
    }

    @Override
    public List<OAuth2AccessToken> listByCondition(OAuth2AccessTokenCriteria criteria) {
        LambdaQueryWrapper<OAuth2AccessTokenDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(!CollUtil.isEmpty(criteria.getAccessTokenIds()), OAuth2AccessTokenDO::getId, criteria.getAccessTokenIds());

        wrapper.eq(!StrUtil.isEmpty(criteria.getAccessToken()), OAuth2AccessTokenDO::getAccessToken, criteria.getAccessToken());
        wrapper.eq(criteria.getClientId() != null, OAuth2AccessTokenDO::getClientId, criteria.getClientId());
        wrapper.eq(criteria.getUserId() != null, OAuth2AccessTokenDO::getUserId, criteria.getUserId());

        List<OAuth2AccessTokenDO> accessTokenDOS = mapper.selectList(wrapper);
        if (CollUtil.isEmpty(accessTokenDOS)) {
            return new ArrayList<>();
        }
        return convertor.toDomainObjects(accessTokenDOS);
    }

    public Map<Long, List<OAuth2AccessToken>> getAllValidTokens(){
        // 1. 获取所有有效的访问令牌
        LambdaQueryWrapper<OAuth2AccessTokenDO> wrapper = new LambdaQueryWrapper<>();
        List<OAuth2AccessTokenDO> validTokens = mapper.selectList(wrapper);

        if (CollUtil.isEmpty(validTokens)) {
            return new HashMap<>();
        }

        // 2. 过滤出未过期的令牌
        LocalDateTime now = LocalDateTime.now();
        List<OAuth2AccessTokenDO> activeTokens = validTokens.stream()
                .filter(token -> token.getExpiresTime() != null && token.getExpiresTime().isAfter(now))
                .collect(Collectors.toList());

        List<OAuth2AccessToken> domainObjects = convertor.toDomainObjects(activeTokens);
        // 3. 按用户ID分组，每个用户可能有多个令牌
        return domainObjects.stream()
                .collect(Collectors.groupingBy(OAuth2AccessToken::getUserId));
    }

    @Override
    public PageResponse<OAuth2AccessToken> pageByCondition(OAuth2AccessTokenCriteria criteria) {
        // 创建查询条件
        LambdaQueryWrapper<OAuth2AccessTokenDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(!CollUtil.isEmpty(criteria.getAccessTokenIds()), OAuth2AccessTokenDO::getId, criteria.getAccessTokenIds());

        LocalDateTime[] createTime = criteria.getCreateTime();
        if (createTime != null) {
            if (createTime.length == 1 && createTime[0] != null) {
                wrapper.ge(OAuth2AccessTokenDO::getCreateTime, createTime[0]);
            } else if (createTime.length > 1 && createTime[0] != null && createTime[1] != null) {
                wrapper.between(OAuth2AccessTokenDO::getCreateTime, createTime[0], createTime[1]);
            }
        }

        wrapper.eq(!StrUtil.isEmpty(criteria.getAccessToken()), OAuth2AccessTokenDO::getAccessToken, criteria.getAccessToken());
        wrapper.eq(criteria.getClientId() != null, OAuth2AccessTokenDO::getClientId, criteria.getClientId());
        wrapper.eq(criteria.getUserId() != null, OAuth2AccessTokenDO::getUserId, criteria.getUserId());

        // 创建分页对象，确保使用 OAuth2AccessTokenDO
        Page<OAuth2AccessTokenDO> page = new Page<>(criteria.getPageIndex(), criteria.getPageSize());

        // 排序字段
        if (!StrUtil.isEmpty(criteria.getOrderBy())) {
            if (PageQuery.DESC.equals(criteria.getOrderDirection())) {
                page.addOrder(OrderItem.desc(criteria.getOrderBy()));
            } else {
                page.addOrder(OrderItem.asc(criteria.getOrderBy()));
            }
        }
        mapper.selectPage(page, wrapper);
        List<OAuth2AccessToken> accessTokens = convertor.toDomainObjects(page.getRecords());
        return PageResponse.of(accessTokens, (int)page.getTotal(), (int)page.getSize(), (int)page.getCurrent());
    }
}